\documentclass{article}

\usepackage{ctex}
\usepackage{geometry}
\geometry{a4paper, scale=0.85} % 设置为 A4 纸，边距为 1 英寸
\usepackage{graphicx} % 插入图片
\graphicspath{{D:/文章中枢/课程讲义/模拟电子技术-基于Multisim实现/Image}} % 图片路径
\usepackage{subcaption} 
\usepackage{amsmath} % 数学公式
\usepackage{fancyhdr} % 页眉页脚
\usepackage{circuitikz} % 电路图
\usepackage{tikz} % 绘图
\usepackage{float} % 图片浮动
\usepackage{listings} % 代码高亮
\usepackage{color} % 颜色
\usepackage{hyperref} % 超链接
\usepackage{siunitx} % SI单位 
\usepackage{comment}
\usepackage{sectsty}

\sectionfont{\centering}
\subsectionfont{\centering}

\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.9,0.95,1.0} % 修改为淡蓝色背景

\lstset{
    backgroundcolor=\color{backcolour},   % 背景色
    commentstyle=\color{codegreen},        % 注释样式
    keywordstyle=\color{magenta},          % 关键字样式
    numberstyle=\tiny\color{codegray},     % 行号样式
    stringstyle=\color{codepurple},        % 字符串样式
    basicstyle=\ttfamily\small,           % 基本字体样式
    breakatwhitespace=false,               % 仅在空白处换行
    breaklines=true,                       % 自动换行
    captionpos=b,                          % 标题位置(b底部)
    keepspaces=true,                       % 保持空格
    numbers=left,                          % 行号位置
    numbersep=5pt,                         % 行号与代码的距离
    showspaces=false,                      % 显示空格
    showstringspaces=false,                % 显示字符串中的空格
    showtabs=false,                        % 显示制表符
    tabsize=2,                             % 制表符宽度
    frame=single,                          % 边框样式
    rulecolor=\color{black},              % 边框颜色
}

\lstset{
    backgroundcolor=\color{backcolour},   % 背景色
    commentstyle=\color{codegreen},        % 注释样式
    keywordstyle=\color{magenta},          % 关键字样式
    numberstyle=	\color{codegray},     % 行号样式
    stringstyle=\color{codepurple},        % 字符串样式
    basicstyle=	tfamily\small,           % 基本字体样式
    breakatwhitespace=false,               % 仅在空白处换行
    breaklines=true,                       % 自动换行
    captionpos=b,                          % 标题位置(b底部)
    keepspaces=true,                       % 保持空格
    numbers=left,                          % 行号位置
    numbersep=5pt,                         % 行号与代码的距离
    showspaces=false,                      % 显示空格
    showstringspaces=false,                % 显示字符串中的空格
    showtabs=false,                        % 显示制表符
    tabsize=2,                             % 制表符宽度
    frame=single,                          % 边框样式
    rulecolor=\color{black},              % 边框颜色
}

% 添加汇编语言支持
\lstdefinelanguage{assembly}{
  morekeywords={MOV, ADD, SUB, MUL, DIV, INC, DEC, PUSH, POP, CALL, RET, JMP, JZ, JNZ, JC, JNC, CMP, AND, OR, XOR, NOT, LOOP, LEA}, % 汇编指令
  morecomment=[l];, % 行注释符号
  morecomment=[s]{/*}{*/}, % 块注释符号
  morestring=[b]", % 字符串定义
  sensitive=true % 区分大小写
}

\begin{document}

\begin{titlepage}
    \centering
    {\Huge 计算机编程基础讲义 \\[1cm]}
    {\Large 基于 C/C++ 实现 \\[2cm]}
    {\Large 笛芦-Fumo\_B\_King \\[1cm]}
    {\large \today}
\end{titlepage}


\tableofcontents        % 生成目录
\newpage                % 换页

\section{前言}
\newpage
    \subsection{为什么要学习计算机编程}
    \subsection{计算机编程学习内容与层次结构}
    \subsection{本书特点}
    \subsection{本书使用的工具}
        本书采用的工具为Visual Studio 2022和Visual Studio Code两款软件。同时需要配置C/C++环境。
        包括\texttt{gcc}(GNU \texttt{C}编译器)、\texttt{g++}(GNU \texttt{C++}编译器)、
        \texttt{CMake}(跨平台构建系统)和\texttt{gdb}(GNU调试器)等工具链。

    \subsection{本书的相关习题}

\newpage
\section{计算机拓扑结构}
    现在我们写一串代码，看看可以输出哪些内容：
    \begin{lstlisting}[language=C++, breaklines=true, basicstyle=\ttfamily\small]
        #include <iostream>
        #include <string>

        class __Student{
            public:
                __Student(std::string name, int age, bool sex, 
                          double height, double weight) : 
                    name(name), age(age), sex(sex), 
                    height(height), weight(weight){
                    std::cout << "构造了一个学生" << std::endl;
                }
                
                ~__Student(){
                    std::cout << "销毁了一个学生" << std::endl;
                }
                
                std::string name;
                int age;
                bool sex;
                double GetHeight() { return this->height; }
                double GetWeight() { return this->Weight; }
                
            private:
                double height;
                double weight;
        };

        int main() {
            __Student LyHua("李华", 18, 0, 175.4, 67.7);
            std::cout << LyHua.GetHeight() << std::endl;
            return 0;
        }
    \end{lstlisting}
    这段代码输出的内容会是什么呢？我们使用g++编译即可获得代码输出内容，输入以下命令：
    \begin{lstlisting}[language=bash, breaklines=true, basicstyle=\ttfamily\small]
        g++ -g student.cpp -o student.exe
        ./student.exe
    \end{lstlisting}
    输出内容为：
    \begin{lstlisting}[language=bash, breaklines=true, basicstyle=\ttfamily\small]
        构造了一个学生
        175.4
        销毁了一个学生
    \end{lstlisting}
    我们的任务是研究这些代码是如何写的？如何运行的？我们怎么可以利用计算机编程去实现我们想要的功能？

    \newpage
    \subsection{计算机组成内容}
        拆开一台计算机我们可以发现，在计算机内部，有很多的硬件组件，比如CPU、内存、硬盘、显卡、声卡、网卡等。
        这些硬件组件都有自己的功能，比如CPU负责处理指令，内存负责存储数据，硬盘负责存储文件等。这些硬件组件都
        通过总线连接在一起，总线是一种用于连接计算机内部组件的通信渠道。总线可以分为地址总线、数据总线和控制总
        线三种类型。地址总线用于传输地址信息，数据总线用于传输数据信息，控制总线用于传输控制信号。总线的宽度决
        定了它可以同时传输的信息数量，一般来说，总线的宽度越大，传输速度就越快。总线的宽度一般为32位或64位，这
        取决于计算机的体系结构。32位计算机的总线宽度为32位，64位计算机的总线宽度为64位。总线的宽度越大，计算机
        的性能就越好。

        下图是计算机结构的抽象图：
        \begin{figure}[H]
            \centering
            \begin{tikzpicture}[scale=0.8]
                % 定义节点样式
                \tikzset{
                mainblock/.style={draw, rectangle, minimum width=3cm, minimum height=1.5cm, text centered, fill=blue!10},
                subblock/.style={draw, rectangle, minimum width=2cm, minimum height=1cm, text centered, fill=blue!5},
                component/.style={draw, rectangle, minimum width=1.5cm, minimum height=0.8cm, text centered},
                conn/.style={->, >=stealth, thick}
                }
                % 主节点
                \node[mainblock] (system) at (0, 0) {计算机系统};
                % 子节点 - 处理器
                \node[subblock] (processor) at (-6, -2.5) {处理器};
                \node[component] (alu) at (-7.5, -4.5) {ALU};
                \node[component] (cu) at (-4.5, -4.5) {控制器};
                \node[component] (reg) at (-6, -6) {寄存器};
                % 子节点 - 存储系统
                \node[subblock] (memory) at (0, -2.5) {存储系统};
                \node[component] (ram) at (-1.5, -4.5) {RAM};
                \node[component] (rom) at (1.5, -4.5) {ROM};
                \node[component] (cache) at (0, -6) {Cache};
                % 子节点 - I/O系统
                \node[subblock] (io) at (6, -2.5) {I/O系统};
                \node[component] (input) at (4.5, -4.5) {输入设备};
                \node[component] (output) at (7.5, -4.5) {输出设备};
                \node[component] (storage) at (6, -6) {外存};
                % 总线
                \node[draw, thick, rectangle, minimum width=10cm, minimum height=0.5cm, fill=yellow!20] (bus) at (0, -7.5) {系统总线};
                % 连线
                \draw[conn] (system) -- (processor);
                \draw[conn] (system) -- (memory);
                \draw[conn] (system) -- (io);
                
                \draw[conn] (processor) -- (alu);
                \draw[conn] (processor) -- (cu);
                \draw[conn] (processor) -- (reg);
                
                \draw[conn] (memory) -- (ram);
                \draw[conn] (memory) -- (rom);
                \draw[conn] (memory) -- (cache);
                
                \draw[conn] (io) -- (input);
                \draw[conn] (io) -- (output);
                \draw[conn] (io) -- (storage);

                \draw[conn] (reg) -- (bus);
                \draw[conn] (cache) -- (bus);
                \draw[conn] (storage) -- (bus);
            \end{tikzpicture}
            \caption{计算机系统详细结构框图}
            \label{fig:computer_system}
        \end{figure}

        \subsubsection{CPU与运算系统}
            我们需要明确一点：什么叫做CPU？CPU为中央处理器，负责执行指令和处理数据，所以对于一个CPU，
            有着指令处理器和算数处理器，指令处理器我们称为控制器，而算数处理器我们称为ALU(算术逻辑单元)。
            控制器负责从内存中读取指令，然后根据指令的操作码，将指令分发给不同的组件进行执行。而ALU负责执行
            算术和逻辑运算，比如加法、减法、乘法、除法、与、或、非等。

            上面有些新词语，比如上面是指令，什么是与或非等？让我们捋一下这里的关系与内容：指令就是让计算机去
            做出什么操作，比如加减乘除或者是让数据换个地方。与或非就是基本的逻辑运算，与算数运算不同，逻辑运
            算很简单，我们在下面会列出来：
            \begin{itemize}
                \item 与：在手写上用$\cdot$去表示，在计算机系统用\&去表示，运算为两个逻辑都为1时为1，否则为0。
                        $$1\&1=1$$
                        $$1\&0=0$$
                        $$0\&1=0$$
                        $$0\&0=0$$
                \item 或：在手写上用$+$去表示，在计算机系统用|去表示，运算为两个逻辑有一个为1时为1，否则为0。
                        $$1|1=1$$
                        $$1|0=1$$
                        $$0|1=1$$
                        $$0|0=0$$
                \item 非：在手写上用$'$去表示，在计算机系统用$\sim$去表示，运算为逻辑为0时为1，逻辑为1时为0。
                        $$\sim 1=0$$
                        $$\sim 0=1$$
                \item 运算优先级：非>与>或，即先算非，再算与，最后算或。
            \end{itemize}
            关于与或非具体内容不再做阐述，想要详细了解参考《数字电子技术基础》。

            让我们来解析一下上述步骤：
            \begin{itemize}
                \item 控制器从内存中读取指令。
                \item 控制器根据指令的操作码，将指令分发给不同的组件进行执行。
                \item 如果指令需要进行算术或逻辑运算，ALU会执行这些运算。
                \item 运算结果会被写回寄存器或内存中，供后续指令使用。
            \end{itemize}
            接下来CPU就对这个操作进行往复进行，比如计算一个求和$\sum_{k=0}^n A_k$，那么CPU就会进行
            抽取加法命令，然后从内存中读取数据，进行加法运算，将结果写回内存中，然后继续抽取下一个指令，
            重复上述过程，直到所有指令都执行完毕。当然这一过程是可以用机器代码进行表示的，我们称作为汇编
            指令：
            \begin{lstlisting}[language=assembly, breaklines=true]
                    AREA SumArray, CODE, READONLY
                    ENTRY
                    EXPORT main
                main
                    MOV R0, #0          ; R0 = 累加器，初始化为0
                    MOV R1, #5          ; R1 = 数组长度n=5
                    LDR R2, =array      ; R2 = 数组首地址
                loop
                    LDR R3, [R2], #4    ; R3 = 加载数组元素，R2自动增加4(ARM中一个字为4字节)
                    ADD R0, R0, R3      ; 累加：R0 = R0 + R3
                    SUBS R1, R1, #1     ; R1 = R1 - 1，并更新标志位
                    BNE loop            ; 如果R1 != 0，跳转到loop继续循环
                    ; 程序结束处理
                    SWI 0x123456        ; 终止程序
                    ; 数据段
                    AREA SumArrayData, DATA, READWRITE
                array
                    DCD 10, 20, 30, 40, 50  ; 定义数组A0到A4的值
                    END
            \end{lstlisting}
            仔细观察这段机器码(即使你读不懂)，会发现：程序开始时将R0置为0，意思是开辟一个内存空间，并给
            0这个数值，随后设置R1长度为5(也就是可以存5个数据)，然后设置R2为数组的首地址，即A0的地址，
            然后进入循环，循环中先将R2指向的内存中的数据加载到R3中，然后将R3中的数据加到R0中，然后R2
            自动增加4(因为每个数据占4个字节)，R1减1，然后判断R1是否为0，如果不为0，就跳转到loop继续循环，
            直到R1为0，循环结束，程序结束。

            这是CPU执行一整个过程，尽管你有很多内容听不懂，这没关系，因为程序的运行不止一个CPU在发力，
            需要注意一点，CPU的运算需要调取数据，CPU本身是帮你存储数据的，不论CPU是执行指令还是运算
            都需要数据告知才可以执行。数据从哪来？

        \subsubsection{内存与数据线}
            内存是CPU能够访问的最基本的存储单元，它用于存储程序和数据。每个内存单元都有一个唯一的地址，
            用于标识该内存单元。CPU通过地址总线将地址发送到内存，然后内存返回对应的数据或指令。

            观察下面的结构图：
            \begin{figure}[H]
                \centering
                \begin{tikzpicture}[scale=1.2, >=latex]
                    % CPU模块
                    \node[rectangle, draw, thick, minimum width=3cm, minimum height=2cm, fill=blue!10] (cpu) at (-4, 0) {\textbf{CPU}};
                    % 内存模块
                    \node[rectangle, draw, thick, minimum width=4cm, minimum height=9cm, fill=green!10] (memory) at (2, 0) {};
                    \node[font=\bfseries] at (memory.north) [above=0.2cm] {内存模块};
                    % 内存内部结构
                    \node[rectangle, draw, thin, minimum width=3.5cm, minimum height=0.8cm] at (2, 3.2) {\footnotesize 内存控制器};
                    \foreach \i in {1,2,...,8} {
                        \node[rectangle, draw, thin, minimum width=3.5cm, minimum height=0.7cm] at (2, 3.2 - 0.8*\i) {\footnotesize 内存单元 (地址: 0x00\i)};
                    }
                    % 地址总线
                    \foreach \i in {1,2, ...,8} {
                        \draw[thin, -latex] (-1, 3.2 - 0.8*\i) -- ++(1.7, 0) node[midway, above] {\tiny 地址线};
                    }
                    \node[rectangle, draw, thick, minimum width=0.4cm, minimum height=0.4cm, fill=red!10] at (-1.15, 0) {};
                    \draw[thick] (-1, 0) -- ++(-1.75, 0) node[midway, above] {\normalsize 地址总线};
                    \draw[thick] (-1, 2.4) -- ++(0, -5.6);
                    % 数据总线
                    \foreach \i in {1,2, ...,8} {
                        \draw[thin, latex-latex] (0.45+0.35*\i, -3.5) -- ++(0, -1) node[midway, yshift=3pt, sloped] {\tiny 数据线};
                    }
                    \draw[thick] (3.25, -4.5) -- ++(-7, 0) -- ++(0, 3.65) node[midway, yshift=5pt, sloped] {\normalsize 数据总线};
                    % 控制总线
                    \draw[thick, latex-] (0.7, 3.2) -- ++(-4.5, 0) -- ++(0, -2.35) node[midway, yshift=5pt, sloped] {\textbf 控制总线};
                    % 图例
                    \node[rectangle, draw, thick, minimum width=0.5cm, minimum height=0.5cm, fill=blue!10] at (-7, 2) {};
                    \node at (-6, 2) {CPU};
                    \node[rectangle, draw, thick, minimum width=0.5cm, minimum height=0.5cm, fill=red!10] at (-7, 1.5) {};
                    \node at (-6, 1.5) {总线译码器};
                    \node[rectangle, draw, thick, minimum width=0.5cm, minimum height=0.5cm, fill=green!10] at (-7, 1) {};
                    \node at (-6, 1) {内存};
                    \draw[thick] (-7.25, 0.5) -- (-6.75, 0.5);
                    \node at (-6, 0.5) {总线};
                \end{tikzpicture}
                \caption{计算机内存模型}
                \label{fig:computer_memory}
            \end{figure}
            应该注意到，CPU对内存的控制是分为三条总线的，其中包括地址总线、数据总线和控制总线。地址总线用于
            发送CPU要访问的内存单元的地址，数据总线用于发送或接收CPU与内存之间的数据，控制总线用于发送CPU
            对内存的控制信号。我们讲解每一个总线的作用。
            \begin{itemize}
                \item 控制总线：控制总线用于发送CPU对内存的控制信号，例如读写信号、时钟信号等，这部分和我们
                    学习编程语言关系不大，可以不必了解，具体内容参考《数字电子技术基础》和《计算机组成原理》
                \item 数据总线：数据总线是存放/读取内存上的数据位的总线，每一个数据位可以存放一个bit的数据
                    这个数据只有两种状态，0或1。那么八位数据宽度的内存可以存放八位数据位的数据，例如
                    0b11011000(0b表示的是二进制数字)。但是访问这个内存的数据的前提是这个数据被选中才
                    可以，因为有很多个内存单元，不可能每一个都会引出八个数据位，所以实际控制是八个数据位
                    串起全部的内存单元，然后选中谁读取谁。
                \item 地址总线：地址总线控制内存地址从而对地址上的数据进行访问，总线的宽度一般为32位或64位
                    为主(现代计算机多为64位)，其中地址总线和地址线有一个地址总线译码器，假设一个计算机内存
                    大小为4GB，假设数据线宽度为8位，那么需要地址线数量有$2^{32}$这么多
                    的地址线，每个地址线可以表示0或1，所以需要$2^{32}$位的地址线，但是地址总线是不可能
                    这么多的，毕竟现在主流CPU的针脚也才一千多个，那么如何解决这多内存地址的问题？
                \item 地址总线译码器：实际上现代x64处理器配合64位系统只会引出64个地址总线，也就是总线
                    宽度也才64位(上文提到)，但是我们一次也只会访问一个地址，不会出现一次访问多个地址的情
                    况，比如有三位宽度的总线000，那么000代表第一位地址，001代表第二位地址...，所以
                    三位宽度最大可以访问八位地址，那么32位总线最大可以访问$2^{64}$位地址，最大访问
                    内存为$2^{64}$GB，也就是16EB(1EB=1024PB)。实现把地址总线映射到单个地址线的
                    的功能模块称为地址总线译码器。
            \end{itemize}
            计算机内存的实际结构是什么样的呢？我们观察现代主流的DDR的内存内部结构图：
            \begin{figure}[htbp]
                \centering
                \begin{tikzpicture}[scale=1.2]
                    % 绘制坐标轴
                    \draw (-0.5, 0) -- (14, 0) node[right] {字线};
                    \draw (-0.5, -3) -- (14, -3) node[right] {字线};
                    \draw (-0.5, -6) -- (14, -6) node[right] {字线};
                    % 绘制晶体管T
                    \foreach \i in {0, 1, 2, ..., 7} {
                        \draw (1.5+1.7*\i, -1) node[nmos,rotate=-90 ,anchor=D] (nmos) {};
                        \draw (nmos.G) to [short, -*] (0.85+1.7*\i, 0);  % 栅极
                        \draw (nmos.S) to [short, -*] ++(-0.2, 0);
                        \draw (nmos.D) to [C, l_=$C_{s\i}$, bipoles/length=0.5cm] ++(0, -0.8) to [short, -] ++(0, -0.2);
                        \draw (1.5+1.7*\i, -2) -- (1.5+1.7*\i, -2) node [ground](1.5+1.7*\i, -2) {};
                    }
                    \foreach \i in {0, 1, 2, ..., 7} {
                        \draw (1.5+1.7*\i, -4) node[nmos,rotate=-90 ,anchor=D] (nmos) {};
                        \draw (nmos.G) to [short, -*] (0.85+1.7*\i, -3);  % 栅极
                        \draw (nmos.S) to [short, -*] ++(-0.2, 0);
                        \draw (nmos.D) to [C, l_=$C_{s\i}$, bipoles/length=0.5cm] ++(0, -0.8) to [short, -] ++(0, -0.2);
                        \draw (1.5+1.7*\i, -5) -- (1.5+1.7*\i, -5) node [ground](1.5+1.7*\i, -5) {};
                    }
                    \foreach \i in {0, 1, 2, ..., 7} {
                        \draw (1.5+1.7*\i, -7) node[nmos,rotate=-90 ,anchor=D] (nmos) {};
                        \draw (nmos.G) to [short, -*] (0.85+1.7*\i, -6);  % 栅极
                        \draw (nmos.S) to [short, -*] ++(-0.2, 0);
                        \draw (nmos.D) to [C, l_=$C_{s\i}$, bipoles/length=0.5cm] ++(0, -0.8) to [short, -] ++(0, -0.2);
                        \draw (1.5+1.7*\i, -8) -- (1.5+1.7*\i, -8) node [ground](1.5+1.7*\i, -8) {};
                    }
                    \foreach \i in {0, 1, 2, ..., 7} {
                        \draw (0+1.7*\i, 0.5) -- (0+1.7*\i, -12) node[right] {位线$A_\i$};
                        \draw (0+1.7*\i, -9.5) -- (1+1.7*\i, -9.5);
                        \draw (1+1.7*\i, -9.5) to [C, l_=$C_{b\i}$] (1+1.7*\i, -10.5);
                        \draw (1+1.7*\i, -10.5) -- (1+1.7*\i, -11) node [ground](1, -11) {};
                    }
                \end{tikzpicture}
                \caption{SDRAM存储单元基本结构}
                \label{fig:sdram_cell}
            \end{figure}
            
            我们观察可以发现，内存的结构是由字线(地址线)和位线(数据线构成)，并且每个内存单元共享一组字线
            内存模块共享八位位线。当使用地址线选中当前的内存单元时，位线才会控制内存单元的数据进出，而
            其他内存模块不受影响(因为没有被地址线选中所以操作是无效的)，这样就实现的数据的存储与读取。当
            然会发现这里面每个单元有一个电容，每个位线有一个大电容(实际上个MOS管都会挂一个电容，这里的图
            省略了)。利用电容存储才可以进行数据的存储与读取。缺点是集成电路的集成电容会漏电，所以需要不断
            的刷新进行充电来保持数据。所以内存是由刷新频率的，刷新期间是不可以访问内存的，所以在读写速度
            上，内存虽然很快，但是受到刷新频率的影响，会限制读写最高频率，购买内存条时会发现内存条有一个
            参数就是读写频率。同时内存条断电数据丢失，是一种易失性存储器。
        
        \subsubsection{ROM与RAM}
            上一章节说到，内存条是SDRAM为基础单元进行存储的(具体内容学习《计算机组成原理》《数字电子技
            术基础》)，其本质就是一个RAM类存储器，而存储器分为RAM和ROM。RAM和ROM的区别在于，RAM是
            随机访问存储器，而ROM是只读存储器。RAM可以读写，而ROM只能读。但是很遗憾的是这个定义已经
            与时代完全脱节，因为ROM不可读已经是上个世纪八十年代时候的事情了，所以具体的定义是：而ROM
            是只读存储器具有掉电不丢失的特点，称作非易失性存储器；RAM是随机存储器具有掉电丢失的特点，
            称作易失性存储器。

            我们简述一下ROM的发展史：
            \begin{itemize}
                \item ROM：只读存储器，存储内容在光刻机加工时的掩膜工艺就已经被确定，是最早用于存储
                    程序内容的存储器。
                \item PROM：可编程只读存储器，用户可以通过编程将自己的程序写入其中，但是只能写入一次，
                    后续不能再修改，因为具有很强的不可更改性，用于一些老旧产品或特殊产品中。
                \item EPROM(UV-ROM)：紫外线可擦除可编程只读存储器，用户可以通过紫外线擦除其中的内容，
                    然后再写入新的程序，因为擦除工艺麻烦，现已被EEPROM淘汰。
                \item EEPROM：电可擦除可编程只读存储器，用户可以通过电信号擦除其中的内容，然后再写入新的
                    程序，但因为制造工艺复杂，所以成本较高，并且容量不会特别大。
            \end{itemize}
            现代主流的ROM采用的都是EEPROM，因为其成本较低，容量也比较大，所以被广泛应用于各种电子设备中。
            常见的电脑主板启动时候的BIOS系统启动文件就写在EEPROM中，所以实际上可以通过重新擦除并烧录
            EEPROM实现电脑主板的更新。
            \begin{itemize}
                \item RAM：随机访问存储器，用户可以随机访问其中的任意一个存储单元，读写速度快，但是
                    掉电后数据丢失，这是一个大类的名称，下面的存储器都可以称作为RAM。
                \item SRAM：静态随机访问存储器，利用电路门实现的结构，一般是SR锁存器或是JK寄存器
                    用户可以随机访问其中的任意一个存储单元，因为不存在刷新周期，可以按位擦写读写速
                    度极快，一般用在CPU缓存之中。
                \item DRAM：动态随机访问存储器，用户可以随机访问其中的任意一个存储单元，因为存在刷新
                    问题，所以读写速度慢，但是成本低，容量大，是作为内存的基本结构，但后来被SDRAM淘汰
                \item SDRAM：同步动态随机访问存储器，用户可以随机访问其中的任意一个存储单元，与CPU
                    进行同步工作，可以解决DRAM因为刷新导致时钟不匹配的情况，提高了刷新速度，是现代的
                    主流内存基本结构。
                \item DDR：双数据率动态随机访问存储器，用户可以随机访问其中的任意一个存储单元，本质上
                    还是SDRAM，只是可以在一个时钟周期写入两次数据，是现在电脑内存条的主要选择。
            \end{itemize}          
            现代内存条技术采用的为DDR4到DDR5技术，后缀数字为第几代技术，内存是用于数据处理与交换的寄存
            地方，但是CPU的数据并不是直接与内存进行交换，而是通过CPU缓存技术实现，在购买CPU时，不仅要
            关注几核几线程以及频率，还需要关注CPU的一二三级缓存大小，缓存通过寄存器实现，但寄存器造价
            高昂以及结构复杂，一般大小最大不超过32MB。

            那硬盘是什么？说了半天都是内存也没有我们常说的SSD和HDD。因为硬盘属于外部存储器，将数据存储
            到外部，常用的技术的机械硬盘(HDD)，这是一种纯机械结构硬盘，具体原理查阅网络，这里不赘述。
            现代最常用的是Flash闪存技术，也被称为固态硬盘(SSD)，里面使用存储颗粒，可以随机读写，但是
            擦除时需要按块擦除，由于生产工艺等问题，内部会有坏点，所以读写速度远没有DDR快，但是因为使用
            电荷固化存储技术，所以有着掉电不丢失的特点，数据最长可存储15年之久在不通电的情况下。

        \subsubsection{IO系统}
            电脑还需要由输入设备与输出设备，计算机引出可以实现输入与输出(Input-Output)功能的接口
            称为IO接口，我们的电脑由很多IO接口，比如USB接口、HDMI接口、VGA接口、音频接口等，这些接口
            可以实现电脑与外部设备的连接，比如鼠标、键盘、显示器、音响等。接下来讲解常用的IO：
            \begin{itemize}
                \item USB-C接口：“Type-C”，正反可插，是当前主流形态，支持 USB 3.2(20Gbps)、USB4
                    (40-80Gbps)，还能兼顾供电(如给手机 / 笔记本充电)和视频输出(连接显示器)，
                    适配手机、固态硬盘、显示器、笔记本等。
                \item DP接口：性能强于同代 HDMI，主打 PC 显示器，支持更高分辨率和刷新率(如 DP 1.4
                    支持 8K/30Hz、4K/144Hz)，部分高端显卡、电竞显示器标配；衍生出 “Mini DP”(旧款轻薄本 / 显卡)。
                \item HDMI接口：最常用的视频+音频合一接口，常见于显示器、电视、游戏机，主流版本为
                     HDMI2.0(4K/60Hz)、HDMI2.1(8K/60Hz、4K/120Hz，支持高刷和 VRR 可变刷新率)。
                \item VGA接口：老旧接口，VGA 是模拟信号(画质差，仅支持 1080P 以下)，DVI 是数字信号
                    (分 DVI-D(仅视频)、DVI-I(视频 + 模拟))，现多被 HDMI/DP 取代，仅旧设备留存。
                \item SATA接口：机械硬盘(HDD)和普通固态硬盘(SATA SSD)的标准接口，分数据接口
                    (扁线)和 供电接口(大 4pin)，传输速率最高6Gbps，是台式机/笔记本内置存储
                    的主流选择。
                \item M.2接口：超小型接口，专为轻薄设备设计，支持SATA协议或NVMe协议(后者速率极高，
                    如 PCIe4.0NVMeSSD可达7000MB/s以上)，常见于笔记本、台式机主板(需搭配对应
                    协议的 SSD)。
                \item 音频接口：3.5mm 耳机接口：通用 “圆孔”，支持耳机(输出)或麦克风(输入)，部分设备分
                    耳机孔和麦克风孔，部分是二合一孔(需搭配对应耳机)。
                \item RJ45网线接口：网线接口，用于连接有线网络，传输速率分百兆(100Mbps)、千兆(1Gbps)、
                    万兆(10Gbps)，常见于台式机、路由器、交换机，稳定性优于WiFi。
            \end{itemize}
            电脑的IO是由CPU总线传递出去，但是总线连接到电脑主板的南桥，南桥负责将CPU总线转换为IO接口
            进行输入与输出操作。
        
    \newpage
    \subsection{计算机编程实现}
        现在我写一串C语言代码，我们来分析一下它的运行过程：
        \begin{lstlisting}[language=C++, breaklines=true, basicstyle=\ttfamily\small]
            #include <stdio.h>
            int main() {
                int sum = 0;
                int A[5];
                for (int i = 0; i < 5; i++) {
                    scanf("%d", A[i]);
                }
                int i;
                for(i = 0; i < 5; i++) {
                    sum += A[i];
                }
                printf("%d", sum);
                return 0;
            }
        \end{lstlisting}
        当然，这个代码的内容很简单，就是输入5个整数，然后输出它们的和。
        
        \subsubsection{计算机的数值表示方法}
            计算机由以下几类数值：
            \begin{itemize}
                \item 整数：包括正整数、负整数和零。
                \item 浮点数：包括单精度浮点数和双精度浮点数。
                \item 字符：包括字母、数字、标点符号和空格等。
                \item 布尔值：只有两个值，true和false。
                \item 数组：可以存储多个相同类型的数值。
            \end{itemize}
            
            整数：整数是一种没有小数部分的数值，它可以是正整数、负整数或零。在计算机中，整数
            采用二进制编码表示为这样：
            $$ A = \sum_m^n a_i \cdot 2^i $$
            光是看这个肯定是不明白的，我们先从十进制编码下手：
            $$ A = \sum_m^n a_i \cdot 10^i $$
            比如，我们要表示整数1234，我们可以这样写：
            $$ 1234 = 1 \cdot 10^3 + 2 \cdot 10^2 + 3 \cdot 10^1 + 4 \cdot 10^0 $$
            我们可以把这个公式推广到二进制编码：
            $$ A = \sum_m^n a_i \cdot 2^i $$
            比如，我们要表示整数10，我们可以这样写：
            $$ 10 = 1 \cdot 2^3 + 0 \cdot 2^2 + 1 \cdot 2^1 + 0 \cdot 2^0 $$
            比如，我们要表示小数1.25，我们可以这样写：
            $$ 1.25 = 1 \cdot 2^0 + 0 \cdot 2^{-1} + 1 \cdot 2^{-2} $$
            如何实现数值转换呢？在中学时期我们学习过因式分解，我们采用因式分解法即可：
            $$ 1.25 = (1 + 0.25) = 1 \cdot 2^0 + 0.25 \cdot 2^{-1} = 1 \cdot 2^0 + 0 \cdot 2^{-2} + 1 \cdot 2^{-3} $$
            比如，我们要表示小数0.75，我们可以这样写：
            $$ 0.75 = 0 \cdot 2^0 + 1 \cdot 2^{-1} + 1 \cdot 2^{-2} $$
            我们学习了十进制转换二进制，自然学习二进制转换十进制套入上述公式即可：
            $$ 100111 = 1 \cdot 2^5 + 0 \cdot 2^4 + 0 \cdot 2^3 + 1 \cdot 2^2 + 1 \cdot 2^1 + 1 \cdot 2^0 = 63 $$
            二进制小数自然也很好表示：
            $$ 0.1101 = 0 \cdot 2^0 + 1 \cdot 2^{-1} + 1 \cdot 2^{-2} + 0 \cdot 2^{-3} + 1 \cdot 2^{-4} $$
            除了二进制，我们还可以采用其他编码方法，比如十六进制编码：
            $$ A = \sum_m^n a_i \cdot 16^i $$
            超过十的数字我们使用ABCDEF表示，比如3C，我们可以这样写：
            $$ 3C = 3 \cdot 16^1 + 12 \cdot 16^0 = 60 + 18 = 78 $$
            套入上述定理，我们可以快速学习进制转换了。甚至是任意进制：
            $$ A = \sum_m^n a_i \cdot b^i $$
            二进制转十六进制可以快速转换，参考下表(实际上是BCD编码)：
            \begin{center}
                \begin{tabular}{|c|c|c|}
                    \hline
                    二进制 & 十六进制 & 十进制 \\
                    \hline
                    0000 & 0 & 0 \\
                    0001 & 1 & 1 \\
                    0010 & 2 & 2 \\
                    0011 & 3 & 3 \\
                    0100 & 4 & 4 \\
                    0101 & 5 & 5 \\
                    0110 & 6 & 6 \\
                    0111 & 7 & 7 \\
                    1000 & 8 & 8 \\
                    1001 & 9 & 9 \\
                    1010 & A & 10 \\
                    1011 & B & 11 \\
                    1100 & C & 12 \\
                    1101 & D & 13 \\
                    1110 & E & 14 \\
                    1111 & F & 15 \\
                    \hline
                \end{tabular}
            \end{center}
            关于运算，因为二进制的运算与运算实现比较复杂，相关内容参考《数字电子技术基础》。这里不多赘述。
            因为二进制和计算机不能直接计算负数和减法，所以需要采用补码表示法，这里不多陈述。

            上文提到二进制的存储，计算机的一个内存单元可以存储8位二进制数，我们称做一个内存单元为1字节
            这意味着计算机可以存储256个不同的数值，每个数值占用1字节的空间，但是这样还远远不够，
            我们可以让两个字节(内存单元)联合起来，用两个连续的内存单元去表示一个数字，我们称做2字节。
            这意味着计算机可以存储$256^2$个不同的数值，每个数值占用2字节的空间。当然我们还可以用4字节表
            示一个整数，这意味着计算机可以存储$256^4$个不同的数值，每个数值占用4字节的空间。
            因为计算机一般用四个字节就足够了，所以我们写整数一般是四个字节，那么我们就有：
            \begin{itemize}
                \item 字节整型：1字节：$2^{8}$个不同的数值
                \item 短整型：2字节：$2^{16}$个不同的数值
                \item 整形：4字节：$2^{32}$个不同的数值
                \item 长整型：8字节：$2^{64}$个不同的数值
                \item 长长整型：16字节：$2^{128}$个不同的数值
                \item 32位整型：32字节：$2^{256}$个不同的数值
                \item 64位整型：64字节：$2^{512}$个不同的数值
            \end{itemize}
            
            浮点数：浮点数的存储方法遵循IEEE 754标准，这里不多赘述，同样的，浮点数的存储可以通过
            4字节和8字节来表示，分别对应单精度浮点数和双精度浮点数，如果需要精度很高，可以通过提高
            字节数来表示：
            \begin{itemize}
                \item 单精度浮点数：4字节：$2^{23}$个不同的数值
                \item 双精度浮点数：8字节：$2^{52}$个不同的数值
            \end{itemize}

            字符：计算机并不能直接存储字符，但是可以通过用一个数字去表示一个字符，这个数字我们称做字符编码。
            常用的字符编码有ASCII码和Unicode码，这里不多赘述Unicode编码，因为它包含了所有的字符，
            并且是一种通用的字符编码，所以在计算机中被广泛使用。我们介绍ASCII码：
            ASCII码是一种7位字符编码，每个字符占用一个字节，共128个字符。它包含了常用的英文字母、
            数字、标点符号和一些控制字符。例如，字符A的ASCII码是65，字符a的ASCII码是97，字符0的
            ASCII码是48。当然，现代计算机会用两个到四个字节去存储字符，总之，字符只是数字的映射而已。
            
            布尔值：布尔值占用一个字节，只有两个值，分别是0和1，我们可以用它来表示true和false。
            布尔值在计算机中被广泛使用，比如在条件判断和循环中。虽说只有两个值0和1，实际上这个内存
            单元没有数据就是0，只要有数据就是1。

            数组：如果需要连续存储一些数据，比如要存五个数字，那么我们可以一次申请标准长度整数(四字节
            整数)，然后连续申请五个就可以了，那么实际上就是申请20个连续的内存单元(字节)。这样我们每
            移动四个内存单元就是下一个数字了。
        
        \subsubsection{计算机内存}
            每一个内存单元有一个位移编码，64位计算机系统编码长度为$2^64$，每一个数表示一个内存单元，
            每个内存单元的编码是一个64位二进制，为了简单表示，我们采用32位的二进制的十六进制编码表示，
            接下来会展示如何申请计算机内存：
            $$ integer(number1, 4) \rightarrow 0xFFFF00A0 $$
            $$ integer(number2, 4) \rightarrow 0xFFFF00A4 $$
            $$ number1 \Longrightarrow 0x00000A1C $$
            $$ number2 \Longrightarrow 0x0000FFAC $$
            $$ integer(num, 4*5) \rightarrow 0xFFFF00B0 $$
            $$ num+0 \Longrightarrow 0x00000A1C $$
            $$ num+4 \Longrightarrow 0x00000A20 $$
            $$ num+8 \Longrightarrow 0x00000A24 $$
            $$ num+16 \Longrightarrow 0x00000A28 $$
            $$ num+20 \Longrightarrow 0x00000A2C $$
            实际上这一过程计算机会给我们自动处理好，让我们看一下实际上的C语言代码：
            \begin{lstlisting}[language=C++, breaklines=true, basicstyle=\ttfamily\small]
                #include <stdio.h>
                int main() {
                    int number1 = 114;
                    int number2 = 514;
                    int num[5];
                    num[0] = 1;
                    num[1] = 2;
                    num[2] = 3;
                    num[3] = 4;
                    num[4] = 5;
                    printf("%d\n", number1);
                    printf("%d\n", number2);
                    printf("%d\n", num[0]);
                    printf("%d\n", num[1]);
                    printf("%d\n", num[2]);
                    printf("%d\n", num[3]);
                    printf("%d\n", num[4]);
                    return 0;
                }
            \end{lstlisting}
            那么这一个过程就是：
            \begin{itemize}
                \item 申请4字节内存，并以整数的方式去解释，将114存储在内存中(地址由计算机分配)
                \item 申请4字节内存，并以整数的方式去解释，将514存储在内存中(地址由计算机分配)
                \item 申请20字节内存，并以整数的方式去解释，将1,2,3,4,5分别、
                    存储在内存中(每隔4字节存储一个整数)(地址由计算机分配)
                \item 最后通过IO函数printf实现输出在屏幕上
            \end{itemize}

\newpage
\section{IO函数与变量}
\newpage
    
    \subsection{变量}
            







    








        


\end{document}